#include "fldrv_ertc.h"
#include "flapp_clock.h"
#include "flapp_net_slm.h"
#include "flapp_display.h"
#include "flapp_peripheral.h"


#define FOURYEARDAY (365+365+365+366)  //4年一个周期内的总天数（1970~2038不存在2100这类年份，故暂不优化）
#define TIMEZONE    (8)                //北京时区调整 
 
static u8 month_day[12]={31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; //平年 
static u8 Leap_month_day[12]={31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; //闰年 

typedef struct 
{			
	bool fThreeHour;	//3小时更新标准
	bool fChangeDay;	//隔天更新标准
	bool fTokenInvaild;	//token失效标志
	u8 rand_val;			//隔天更新随机分钟
}flapp_clock_para_t;	//通讯获取到的参数存储，有需要就获取

med_time_t	med_time;
rtc_time_t	syn_time;	//3小时同步
rtc_time_t	token_time;
flapp_clock_para_t	clock_para;
// 判断是否是闰年 
// year: 需要判断的年
// return：1：闰年
//        0: 平年 
u8 isLeapYear(uint16_t year)
{
	u8 res=0;
	
	if(year%4 == 0) // 能够被4整除 
	{
		if((year%100 == 0) && (year%400 != 0))	//能够被100整除，但是不能够被400整除 
		{
			res = 0;
		}
		else
		{
			res =1;
		}
	}
	return res;
}


// 计算从1970年1月1日到指定年份的天数
u16 days_before_year(u16 year) {
		u16 y;
    u16 days = 0;
    for (y = 1970; y < year; y++) {
        days += 365 + (isLeapYear(y) ? 1 : 0);
    }
    return days;
}
 
// 计算某月份之前的天数
u16 days_before_month(u16 year, u8 month) {
		u16 m;
    u16 days = 0;
    for (m = 1; m < month; m++) {
        if (m == 2 && isLeapYear(year)) {
            days += 29;
        } else {
            static int months[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
            days += months[m - 1];
        }
    }
    return days;
}
 
// 将年月日转换为自1970年1月1日以来的天数
u16 date_to_days(u16 year, u8 month, u8 day) {
    return days_before_year(year) + days_before_month(year, month) + day;
}


// 将Unix时间戳转换为北京时间
// unixTime: 需要判断的Unix时间戳 
// *tempBeijing:返回的北京时间
// return：none
// note：没对输入参数正确性做判断
void covUnixTimeStp2Beijing(uint32_t unixTime, rtc_time_t *tempBeijing)
{
    uint32_t totleDayNum=0, totleSecNum=0;
    uint16_t remainDayofYear=0, tempYear=0;
    uint8_t *pr=NULL;
    
    totleDayNum = unixTime/(24*60*60); //总天数(注意加括号)
    totleSecNum = unixTime%(24*60*60); //当天剩余的秒速
 
//    memset(tempBeijing, 0x00, sizeof(rtc_time_t));
    
    // 1.先计算时间 HH:MM:SS
    tempBeijing->u8Hour = totleSecNum/3600;
    tempBeijing->u8Min = (totleSecNum%3600)/60; //error：变量搞错
    tempBeijing->u8Sec = (totleSecNum%3600)%60;
 
    // 2.对时间进行时区调整（注意：这里可能造成日期 +1）
    tempBeijing->u8Hour +=TIMEZONE; 
    if(tempBeijing->u8Hour>23){
        //printf("modify day..\n");
        tempBeijing->u8Hour -= 24;
        remainDayofYear++;  // 日期+1
    }
 
    // 3.计算哪一年
    tempBeijing->u16Year = 1970 + (totleDayNum / FOURYEARDAY) * 4;   // 4年为一个周期
    remainDayofYear += totleDayNum % FOURYEARDAY;
    
    //printf("year:%d, day:%d.\n", tempBeijing->u16Year, remainDayofYear);
    tempYear = isLeapYear(tempBeijing->u16Year)?366:365;
    while(remainDayofYear >= tempYear)  // 计算4年整数倍外的年。
    {
        tempBeijing->u16Year++;
        remainDayofYear -= tempYear;
        tempYear = isLeapYear(tempBeijing->u16Year)?366:365;
    }
    
    // 4.计算哪一月的哪一天
    pr = isLeapYear(tempBeijing->u16Year)?Leap_month_day:month_day;
    remainDayofYear++;          // 这里开始计算具体日期。remainDayofYear为 0 时其实是 1 号，所以这里要 +1
    while(remainDayofYear > *(pr+tempBeijing->u8Mon))
    {
		remainDayofYear -= *(pr+tempBeijing->u8Mon);
        tempBeijing->u8Mon++;
    }
    //printf("year:%d, day:%d.\n", tempBeijing->u16Year, remainDayofYear);
    tempBeijing->u8Mon++; //month
    tempBeijing->u8Day = remainDayofYear;  //day
}
 
// 将北京时间转换为Unix时间戳 
// year: 需要判断的年 
// return：Unix时间戳（从1970/1/1 00:00:00 到现在的秒数) 
// note：没对输入参数正确性做判断
uint32_t covBeijing2UnixTimeStp(rtc_time_t *beijingTime)
{
	uint32_t daynum=0, SecNum=0; //保存北京时间到起始时间的天数
	uint16_t tempYear=1970, tempMonth=0;
 
 
	//1.年的天数 
	while(tempYear < beijingTime->u16Year) 
	{
		if(isLeapYear(tempYear)){
			daynum += 366;
		}
		else{
			daynum += 365;
		}
		tempYear++;
	}
	//2.月的天数
 	while(tempMonth < beijingTime->u8Mon-1) 
 	{
        if(isLeapYear(beijingTime->u16Year)){ //闰年
            daynum += Leap_month_day[tempMonth];
        }
        else{
		    daynum += month_day[tempMonth];
        }
		tempMonth++;
	}
    //3.天数
	daynum += (beijingTime->u8Day-1);
 
    //4.时分秒
    SecNum = daynum*24*60*60; //s    
    SecNum += beijingTime->u8Hour*60*60;    
    SecNum += beijingTime->u8Min*60;    
    SecNum += beijingTime->u8Sec;
 
    //5.时区调整
    SecNum -= TIMEZONE*60*60;
 
    return SecNum;


}

u8 CaculateWeekDay(int y,int m, int d)
{
	u8 iWeek;
	if(m==1||m==2) 
	{
		m+=12;
		y--;
	}

	iWeek=(d+2*m+3*(m+1)/5+y+y/4-y/100+y/400)%7;
	
	return iWeek;
}
/**
	* @brief  初始化时间
	* @param  void
  * @retval none
  */
void flapp_clock_init(void)
{
	clock_para.fTokenInvaild = FALSE;	//上电默认token无效
	clock_para.fThreeHour = TRUE;		//上电默认三小时刷新
	clock_para.fChangeDay = TRUE;
  fldrv_ertc_init();
	fldrv_ertc_alarm_value_set(ALARM_TIME);
}

/**
	* @brief  初始化时间
	* @param  void
  * @retval none
  */
void flapp_clock_power_on_init(void)
{
	clock_para.fTokenInvaild = FALSE;	//上电默认token无效
	clock_para.fThreeHour = TRUE;		//上电默认三小时刷新
	clock_para.fChangeDay = TRUE;
	med_time.bPlayNetSucc = FALSE;
	med_time.u8MedAlarmSta = MED_TYPE_NO;
}
/**
  * @brief  报警时间重装载
  * @param  alam_index : ERTC Alarm Seconds value
  * @retval none
  */
void fldrv_ertc_alarm_value_set(u32 alam_index)
{
	ertc_time_type ertc_time_struct;

	/* disable the alarm */
	ertc_alarm_enable(ERTC_ALA, FALSE);
	ertc_calendar_get(&ertc_time_struct);
	ertc_alarm_set(ERTC_ALA, ertc_time_struct.day, ertc_time_struct.hour, ertc_time_struct.min, (ertc_time_struct.sec + alam_index)% 60, ertc_time_struct.ampm);

	/* disable the alarm */
	ertc_alarm_enable(ERTC_ALA, TRUE);
}
/**
	* @brief  时间戳设置当前时间
	* @param  void
  * @retval none
  */
void flapp_timestamp_to_datetime(u32 timestamp) 
{
	rtc_time_t tempBeijing;
	covUnixTimeStp2Beijing(timestamp,&tempBeijing);

	if(tempBeijing.u16Year > 2000)
	{
		ertc_date_set(tempBeijing.u16Year - 2000,tempBeijing.u8Mon,tempBeijing.u8Day,tempBeijing.u8Week);
		ertc_time_set(tempBeijing.u8Hour, tempBeijing.u8Min, tempBeijing.u8Sec, ERTC_AM);
		
		if(clock_para.fChangeDay)
		{
			clock_para.fChangeDay = FALSE;
			clock_para.rand_val = 0xFF;
		}
		
		if(clock_para.fThreeHour)
		{
			clock_para.fThreeHour = FALSE;
			syn_time = tempBeijing;
		}
		
		if(clock_para.fTokenInvaild == FALSE)
		{
			token_time = tempBeijing;
			clock_para.fTokenInvaild = TRUE;
		}
	}

	fldrv_ertc_alarm_value_set(ALARM_TIME);
}
/**
	* @brief  获取当前时间戳
	* @param  void
  * @retval 时间戳
  */
u32 flapp_get_current_timestp(void)
{
	u32 var;
	rtc_time_t time_stp;
	ertc_time_type time;
	ertc_calendar_get(&time);
		
	time_stp.u16Year = time.year + 2000;
	time_stp.u8Mon = time.month;
	time_stp.u8Day = time.day;
	time_stp.u8Hour = time.hour;
	time_stp.u8Min = time.min;
	time_stp.u8Sec = time.sec;
	var = covBeijing2UnixTimeStp(&time_stp);
	return var;
}
/**
	* @brief  检测服药提醒音频播放
	* @param  void
  * @retval none
  */
void check_med_play()
{
	static confirm_state time = FALSE;
	static u32 play_timestp = FALSE;
	if(med_time.bPlayNetSucc == FALSE){					//播报过网络连接成功
		time = FALSE;
		return;
	}
	
	if(med_time.u8MedAlarmSta != MED_NORMAL){	//只有正常服药才做服药提醒
		time = FALSE;
		return;
	}
	
	if(time == TRUE)
	{
		if(flapp_get_current_timestp() - play_timestp >= TIME_PLAY_ALARM - 2)
			time = FALSE;
	}
	
	if(time == FALSE && med_time.u16RingLen){
		time = TRUE;
		play_timestp = flapp_get_current_timestp();
		med_time.u16RingLen--;
		flapp_set_play_command(VOICE_MEDI_ALA,PLAY_WTICE);
	}
}
/**
* @brief  检测是否3小时更新
	* @param  time
	* @retval void
  */ 
static void check_three_hour_updata(ertc_time_type tim)
{
	u8 day;
	u16 cur_day,updata_day;
	
	if(med_time.bTimeAdj == FALSE)					//时间没校准
		return;
		
	if(clock_para.fThreeHour)
		return;
	
	cur_day = date_to_days(tim.year,tim.month,tim.day);
	updata_day = date_to_days(syn_time.u16Year-2000,syn_time.u8Mon,syn_time.u8Day);
	day = cur_day - updata_day;
	
	if((day*1440+tim.hour*60+tim.min) - (syn_time.u8Hour*60+syn_time.u8Min) >= TIME_3_HOUR)
	{
		clock_para.fThreeHour = TRUE;
		flapp_set_http_req(API_RPT_DEV_PARA,API_3_HOUR);						//上报设备参数，3小时
		flapp_set_http_req(API_GET_CURR_TIME,API_STATUS_MAX);
		flapp_set_http_req(API_GET_TODAY_MED_LOG,API_STATUS_MAX);
		flapp_set_http_req(API_GET_MED_TIME,API_STATUS_MAX);
		flapp_set_http_req(API_GET_TODAY_SUB_LOG,API_STATUS_MAX);
		flapp_set_http_req(API_GET_SUB_TIME,API_STATUS_MAX);
	}
}

/**
* @brief  检测是否隔天更新
	* @param  time
	* @retval void
  */ 
static void check_change_day_updata(ertc_time_type tim)
{
	static u8 rand_run = FALSE;
	u16 cur_day;
	static u16 last_day = 0xffff;
	rand_run++;
	if(med_time.bTimeAdj == FALSE)					//时间没校准
		return;
	if(clock_para.fChangeDay)
		return;
	
	if(0xffff == last_day)	//时间校准过刷新隔天日
		last_day = date_to_days(tim.year+2000,tim.month,tim.day);
	
	cur_day = date_to_days(tim.year+2000,tim.month,tim.day);
	
	if((last_day != cur_day) && (clock_para.rand_val == 0xFF))
	{
		srand(rand_run);
		clock_para.rand_val = (u8)(rand() % 60);
		last_day = cur_day;
		flapp_set_led_sta(DIS_MED,FALSE);				//隔天关闭服药灯
		med_time.u8MedAlarmSta = MED_TYPE_NO;
		flapp_reset_tod_med_info();
		log_debug("change day rand time:%d\r\n",clock_para.rand_val);
		switch(med_time.u8RingSta)
		{
			case 0:med_time.u16RingLen = 0;break;
			case 1:med_time.u16RingLen = 6;break;
			case 2:med_time.u16RingLen = 12;break;
			case 3:med_time.u16RingLen = 300;break;
		}
	}
	
	if(tim.min == clock_para.rand_val)
	{
		log_debug("change day updata\r\n");
		clock_para.fChangeDay = TRUE;
		flapp_set_http_req(API_RPT_DEV_PARA,API_CHANGE_DAY);						//上报设备参数，隔天
		flapp_set_http_req(API_GET_CURR_TIME,API_STATUS_MAX);
		flapp_set_http_req(API_GET_TODAY_MED_LOG,API_STATUS_MAX);
		
		flapp_set_http_req(API_GET_RING_TIME,API_STATUS_MAX);
		flapp_set_http_req(API_GET_VOLUME,API_STATUS_MAX);
		
		flapp_set_http_req(API_GET_MED_TIME,API_STATUS_MAX);
		flapp_set_http_req(API_GET_TODAY_SUB_LOG,API_STATUS_MAX);
		flapp_set_http_req(API_GET_SUB_TIME,API_STATUS_MAX);
	}
}
/**
	* @brief  判断token是否有效
	* @param  time
	* @retval 提醒标志
  */
void check_token_invaild(ertc_time_type time)
{
	u32 var;
	if(med_time.bTimeAdj == FALSE)					//时间没校准
		return;

	var = flapp_get_current_timestp();
	if(var + 12*60*60 >= med_time.u32TokenVaild)	//token提前12小时更新
	{
		COM_PRINTF("token updata!!!\r\n");
		flapp_set_http_req(API_REF_TOKEN,API_STATUS_MAX);
	}
}

static void check_request_para_updata(ertc_time_type time)
{
	check_med_play();
	check_three_hour_updata(time);
	check_change_day_updata(time);
	check_token_invaild(time);
}

void flapp_set_med_para(med_time_t para)
{
	med_time = para;
}

med_time_t flapp_get_med_para()
{
	return med_time;
}
/**
	* @brief  判断是否需要提醒服药
	* @param  time
	* @retval 提醒标志
  */
u8 is_med_need_alarm(ertc_time_type time)
{
	u16 cur_day;
	
	if(med_time.bPlayNetSucc == FALSE){					//播报过网络连接成功
		return MED_TYPE_NO;
	}
	
	if(med_time.bTimeAdj == FALSE)					//时间没校准
		return MED_TYPE_NO;

	if(med_time.u16StartDay == FALSE)				//无服药计划或没绑定
		return MED_TYPE_NO;	
	
	cur_day = date_to_days(time.year + 2000,time.month,time.day);
	
	//log_debug("str:%d end:%d stop_str:%d stop_end:%d cur:%d int:%d\r\n",med_time.u16StartDay,med_time.u16EndDay,med_time.u16StopStrDay,\
																															 med_time.u16StopEndDay,cur_day,med_time.u8Interval);
	if(cur_day < med_time.u16StartDay)			//当前时间小于开始服药时间
		return MED_TYPE_NO;	
	if(cur_day > med_time.u16EndDay)				//当前时间大于结束服药时间
		return MED_TYPE_NO;		
	
	COM_PRINTF("other_case:%d %d %d\r\n",cur_day,med_time.u16StopStrDay,med_time.u16StopEndDay);
	if(cur_day >= med_time.u16StopStrDay && med_time.u16StopStrDay && \
		 cur_day <= med_time.u16StopEndDay && med_time.u16StopEndDay)//当前时间在间断时间区间内
		return MED_TYPE_NO;	
	
	if((cur_day - med_time.u16StartDay) % (med_time.u8Interval + 1))//隔天服药
		return MED_TYPE_NO;	
	
	if(time.hour * 60 + time.min >= med_time.u8Hour * 60 + med_time.u8Min)	//提前三小时服药
	{
		log_debug("normal med\r\n");
		return MED_NORMAL;	
	}
	
	if(time.hour * 60 + time.min + TIME_3_HOUR >= med_time.u8Hour * 60 + med_time.u8Min)	//提前三小时服药
	{
		log_debug("ahead med\r\n");
		return MED_AHEAD;	
	}
	
	return FALSE;
}

/**
* @brief  检测是否需要执行功能
	* @param  time
	* @retval void
  */
static void check_eat_med_remind(ertc_time_type time)
{
	u8 res;
//	COM_PRINTF("tod med:%d",med_time.bTodMedInfo);
	if(med_time.bTodMedInfo)								//有服药记录
	{
		med_time.u8MedAlarmSta = MED_TYPE_NO;
		return;
	}

	if(med_time.u8MedAlarmSta == MED_NORMAL)	//已经在服用提醒中就不做判断了
		return;	
	
	res = is_med_need_alarm(time);
	med_time.u8MedAlarmSta = res;
	switch(res)
	{
		case MED_TYPE_NO:
			flapp_set_led_sta(DIS_MED,FALSE);
			break;
		case MED_NORMAL:
			flapp_set_med_alarm(res);
			flapp_set_led_sta(DIS_MED,0xff);
			flapp_set_led_sta(DIS_MED,TRUE);
			break;
		case MED_AHEAD:
			flapp_set_med_alarm(res);
			flapp_set_led_sta(DIS_MED,FALSE);
			break;
	}
	//log_debug("med_sta:%d %d\r\n",res,med_time.u8MedAlarmSta);
}
/**
* @brief  检测是否需要提醒复诊
	* @param  time
	* @retval 提醒标志
  */
confirm_state is_sub_need_alarm(ertc_time_type time)
{
	sub_para_t	ctrl;
	flapp_get_sub_para_ctrl(&ctrl);
	
	if(med_time.bPlayNetSucc == FALSE)					//播报过网络连接成功
		return FALSE;
	if(ctrl.bTimeAdj == FALSE)		//本地时间校准
		return FALSE;
	if(ctrl.bTodInfo)							//当天无复诊记录
		return FALSE;
	if(ctrl.u16Year == FALSE)			//无复诊日期设置
		return FALSE;
	if(time.hour < 6)							//非复诊周期内
		return FALSE;
	if(time.hour >= 18)	
		return FALSE;
	
	if(ctrl.u16Year - 2000 == time.year && \
		 ctrl.u8Mon == time.month && \
		 ctrl.u8Day == time.day)
	{
		return TRUE;
	}
	return FALSE;
}
/**
* @brief  检测复诊
	* @param  void
  * @retval none
  */
static void check_sub_visit_remind(ertc_time_type time)
{
	confirm_state res;
	res = is_sub_need_alarm(time);
	if(res)
		flapp_set_led_sta(DIS_SUB,TRUE);
	else
		flapp_set_led_sta(DIS_SUB,FALSE);
}
/**
	* @brief  1s一次执行时间运行任务
	* @param  void
  * @retval none
  */
void flapp_clock_run(void)
{
	static u8 temp = 0;
	ertc_time_type time;
	flapp_sys_para_t sys_clock_para;
	
	ertc_calendar_get(&time);
	if(temp == time.sec)  	//1秒钟执行一次
		return;	
  temp = time.sec;
	
	sys_clock_para = flapp_get_sys_para();
	
	if(sys_clock_para.bPowOnOff == FALSE)
		return;
	check_request_para_updata(time);				//3小时和隔天更新
	check_eat_med_remind(time);
	check_sub_visit_remind(time);
	//flapp_ertc_time_show();
}

